package org.xukai.remoting.ftp;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang.CharEncoding;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;
import org.xukai.common.util.UrlUtil;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.TimeZone;

/**
 * @Type FtpFac
 * @Desc ftp执行
 * @author jianyun.zheng
 * @date 2013-12-20
 * @Version V1.0
 */
public class FtpFac {

    private static final SimpleDateFormat defm = new SimpleDateFormat("yyyyMMddHHmmss");
    private static final String CHAR_ENCODING = "GBK";

    private final FTPClient ftpClient;
    private static Logger logger = Logger.getLogger(FtpFac.class);
    private static final String URLSLIP = "/";

    /* *
     * Ftp构造函数
     */
    public FtpFac(FTPData ftpData) throws Exception {
        this.ftpClient = new FTPClient();
        try {
            ftpClient.connect(ftpData.getHost(), Integer.valueOf(ftpData.getPort()));
            // 连接服务器
            ftpClient.login(ftpData.getUserName(), ftpData.getPassWord());
            // 登录
            // 检测是否连接成功
            int reply = ftpClient.getReplyCode();
            // 看返回的值是不是230，如果是，表示登陆成功
            if (FTPReply.isPositiveCompletion(reply)) {
                // 设置字符编码
                ftpClient.setControlEncoding(CHAR_ENCODING);
                // 设置文件传输格式
                ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
                // 设置传输协议
                this.ftpClient.enterLocalPassiveMode();
                this.ftpClient.setBufferSize(1024 * 5);
                this.ftpClient.setDataTimeout(30 * 1000);
                FTPClientConfig ftpClientConfig = new FTPClientConfig(FTPClientConfig.SYST_NT);
                ftpClientConfig.setServerTimeZoneId(TimeZone.getDefault().getID());
                this.ftpClient.configure(ftpClientConfig);
                this.ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
                if (!createDirecroty(ftpData.getPreUrl())) {
                    throw new Exception( "切换ftp根目录失败" + ftpData.getPreUrl());
                }
                return;
            } else {
                ftpClient.disconnect();
            }

            // 关闭FTP连接
        } catch (Exception e) {
            logger.error("登录FTP服务失败|" + "ip:" + ftpData.getRoot() + "|user:" + ftpData.getUserName() + "|pwd:"
                    + ftpData.getPassWord(), e);
        }
        throw new Exception("登录FTP服务失败|" + "ip:" + ftpData.getRoot() + "|user:"
                + ftpData.getUserName() + "|pwd:" + ftpData.getPassWord());
    }

    /**
     * @退出关闭服务器链接
     * */
    public void ftpLogOut() {
        if (null != this.ftpClient && this.ftpClient.isConnected()) {
            try {
                this.ftpClient.logout();// 退出FTP服务器
            } catch (IOException e) {
                e.printStackTrace();
                logger.warn("退出FTP服务器异常！" + e.getMessage());
            } finally {
                try {
                    this.ftpClient.disconnect();// 关闭FTP服务器的连接
                } catch (IOException e) {
                    logger.warn("关闭FTP服务器的连接异常！", e);
                }
            }
        }
    }

    /***
     * 上传Ftp文件
     * 
     * @param localFile 当地文件
     * @param romotUpLoadePath上传服务器路径 - 应该以/结束
     * @throws PreposeException
     * */
    private boolean uploadFile(File localFile) throws Exception {
        FileInputStream inStream = null;
        boolean success = false;
        try {
            if (logger.isDebugEnabled()) {
                logger.debug(localFile.getName() + "开始上传到  " + this.ftpClient.printWorkingDirectory());
            }
            inStream = new FileInputStream(localFile);
            success = this.ftpClient.storeFile(getRemoteFileName(localFile.getName()), inStream);
        } catch (Exception e) {
            throw new Exception("上传文件异常" + localFile.getPath(), e);
        } finally {
            if (inStream != null) {
                try {
                    inStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("上传....." + localFile.getName() + (success ? "成功" : "失败"));
        }
        return success;
    }

    /***
     * @上传文件夹
     * @param localPath 当地文件夹
     * @param remotePathPath Ftp 服务器路径 以目录URLSLIP结束
     * @throws PreposeException
     * */
    public boolean upload(File file, String remote) throws Exception {
        try {
            boolean flag = true;
            if (!file.getName().endsWith(remote)) {
                flag = createDirecroty(remote);
            }
            if (flag) {
                if (file.isDirectory()) {
                    return uploadDirectory(file);
                } else {
                    return uploadFile(file);
                }
            }
        } catch (Exception e) {
            logger.error(e);
        }
        throw new Exception("上传出错：" + file.getAbsolutePath());
    }

    /***
     * @上传文件夹
     * @param localPath 当地文件夹
     * @param remotePathPath Ftp 服务器路径 以目录URLSLIP结束
     * @throws PreposeException
     * */
    private boolean uploadDirectory(File file) throws Exception {
        try {
            String fileName = file.getName();
            this.ftpClient.makeDirectory(fileName);
            // 设置当前目录
            this.ftpClient.changeWorkingDirectory(fileName);
            // 上传文件的列表
            File[] upFiles = file.listFiles();
            for (File upFile : upFiles) {
                synchronized (upFile) {
                    if (upFile.isFile()) {
                        // 文件
                        uploadFile(upFile);
                    } else if (upFile.isDirectory()) {
                        uploadDirectory(upFile);
                    }
                }
            }
            this.ftpClient.changeToParentDirectory();
        } catch (Exception e) {
            throw new Exception("上传出错：" + file.getAbsolutePath() + ":", e);
        }
        return true;

    }

    private String getPath(String url) throws Exception {
        return new String(url.getBytes(CHAR_ENCODING), CharEncoding.ISO_8859_1);
    }

    /**
     * 递归创建远程服务器目录
     * 
     * @param remote
     * @return
     * @throws Exception
     */
    private boolean createDirecroty(String remote) throws Exception {
        if (StringUtils.isBlank(remote)) {
            return true;
        }
        // 目录不存在
        StringTokenizer s = new StringTokenizer(remote, URLSLIP); //
        s.countTokens();
        while (s.hasMoreElements()) {
            String path = this.getPath((String) s.nextElement());
            // 如果服务器上有该目录，不会被创建
            if (!this.ftpClient.changeWorkingDirectory(path)) {
                if (this.ftpClient.makeDirectory(path)) {
                    if (!this.ftpClient.changeWorkingDirectory(path)) {
                        throw new Exception("创建远程目录出错：" + remote);
                    }
                }
            }
        }
        return true;
    }

    public boolean downLoad(String local, String remote) throws Exception {
        FTPFile[] allfile = null;
        logger.info("开始下载：local=" + local + ",remote=" + remote);
        if (null != remote) {
            if (remote.contains("/")) {
                // 以/结尾表示不是文件
                boolean isFile = !remote.endsWith("/");
                int index = remote.lastIndexOf("/");
                String working = remote.substring(0, index);
                this.ftpClient.changeWorkingDirectory(working);
                logger.info("切换工作目录：working=" + working);
                local = UrlUtil.checkUrlEnd(local) + working;
                remote = remote.substring(index + 1);
                if (isFile) {
                    logger.info("请求URL为文件，开始下载文件：lacal=" + local + ",remote=" + remote);
                    return downLoadFile(local, remote);
                }
            }
            allfile = this.ftpClient.listFiles(remote);
        } else {
            allfile = this.ftpClient.listFiles();
        }
        new File(local).mkdirs();
        if (null != allfile && local.length() > 0) {
            for (FTPFile file : allfile) {
                synchronized (file) {
                    if (file.isFile()) {
                        downLoadFile(local, file.getName());
                    } else if (file.isDirectory()) {
                        this.ftpClient.changeWorkingDirectory(file.getName());
                        String localPath = UrlUtil.checkUrlEnd(local) + file.getName();
                        downLoad(localPath, null);
                    } else {
                        logger.info("未正确匹配到类型。。。file=" + file);
                    }
                }
            }
        } else {
            throw new Exception("文件不存在:" + remote);
        }
        this.ftpClient.changeToParentDirectory();
        return true;
    }

    private boolean downLoadFile(String localPath, String fileName) throws Exception {
        // String fileName = file.getName();
        // 覆盖
        String filepath = UrlUtil.checkUrlEnd(localPath) + fileName;
        logger.info("正在下载文件：" + this.ftpClient.printWorkingDirectory() + ":" + fileName + "到" + filepath);
        // 目标ftp文件下载路径
        BufferedOutputStream outStream = null;
        new File(localPath).mkdirs();
        boolean boo = false;
        outStream = new BufferedOutputStream(new FileOutputStream(filepath));
        boo = ftpClient.retrieveFile(this.getPath(fileName), outStream);
        // 从FTP服务器上取回一个文件
        if (null != outStream) {
            outStream.flush();
            // 将缓冲区中的数据全部写出
            outStream.close();
        }
        if (!boo) {
            logger.warn("下载文件:" + this.ftpClient.printWorkingDirectory() + ":" + fileName + "失败");
        } else {
            logger.info("下载文件:" + this.ftpClient.printWorkingDirectory() + ":" + fileName + "成功");
        }
        return boo;
    }

    public boolean write(String remote, Object data) throws IOException {
        PrintWriter pw = new PrintWriter(ftpClient.storeFileStream(remote)); // 写入的文件名
        pw.write(JSON.toJSONString(data));
        pw.flush();
        pw.close();
        return true;
    }

    private static String getRemoteFileName(String localFileName) {
        int index = localFileName.lastIndexOf(".");
        String suffix = "";
        if (index >= 0) {
            suffix = localFileName.substring(index);
        }
        return defm.format(new Date()) + System.nanoTime() + suffix;
    }
}
